﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace HBattle
{
    public class SteeringBehaviors
    {
        public SteeringBehaviors(Vehicle vehicle)
        {
            this.vehicle = vehicle;
        }
        private Vehicle vehicle;

        public Vector3 Seek(Vector3 targetPos)
        {
            Vector3 desiredVelocity = (targetPos - vehicle.CacheTransform.position).normalized * vehicle.MaxSpeed;

            return desiredVelocity - vehicle.Velocity;
        }

        public Vector3 Flee(Vector3 targetPos)
        {
            if ((targetPos - vehicle.CacheTransform.position).sqrMagnitude > 10.0f)
            {
                return Vector3.zero;
            }
            Vector3 desiredVelocity = (vehicle.CacheTransform.position - targetPos).normalized * vehicle.MaxSpeed;

            return desiredVelocity - vehicle.Velocity;
        }

        public Vector3 Arrive(Vector3 targetPos, int deceleration)
        {
            Vector3 toTarget = targetPos - vehicle.CacheTransform.position;

            float dist = toTarget.magnitude;

            if (dist > 0)
            {
                const float DecelerationTweaker = 0.3f;

                float speed = dist / deceleration * DecelerationTweaker;

                speed = Mathf.Min(speed, vehicle.MaxSpeed);

                Vector3 desiredVelocity = toTarget * speed / dist;

                return desiredVelocity - vehicle.Velocity;
            }

            return Vector3.zero;
        }

        public Vector3 Pursuit(Vehicle evader)
        {
            Vector3 ToEvader = evader.CacheTransform.position - vehicle.CacheTransform.position;

            float relativeHeading = Vector3.Dot(vehicle.CacheTransform.forward, evader.CacheTransform.forward);

            if ((Vector3.Dot(ToEvader, vehicle.CacheTransform.forward) > 0 && relativeHeading < -0.95f))
            {
                return Seek(evader.CacheTransform.position);
            }

            float lookAheadTime = ToEvader.magnitude / (vehicle.MaxSpeed + evader.Speed) + TurnAroundTime(vehicle, evader.CacheTransform.position);

            return Seek(evader.CacheTransform.position + evader.Velocity * lookAheadTime);
        }

        public static float TurnAroundTime(Vehicle agent, Vector3 targetPos)
        {
            Vector3 toTarget = (targetPos - agent.CacheTransform.position).normalized;

            float dot = Vector3.Dot(agent.CacheTransform.forward, toTarget);

            const float coefficient = 0.5f;

            return (dot - 1.0f) * -coefficient;
        }


        public Vector3 Evade(Vehicle pursuer)
        {
            Vector3 toPursuer = pursuer.CacheTransform.position - vehicle.CacheTransform.position;

            float LookAheadTime = toPursuer.magnitude / (vehicle.MaxSpeed + pursuer.Speed) + TurnAroundTime(vehicle, pursuer.CacheTransform.position);

            return Flee(pursuer.CacheTransform.position + pursuer.Velocity * LookAheadTime);
        }

        const float wanderRadius = 3f;
        const float wanderDistance = 3f;
        const float wanderJitter = 1.2f;
        Vector3 wanderTarget = new Vector3(wanderRadius, 0f, 0f);

        public Vector3 Wander()
        {
            float r1 = Random.Range(-1f, 1f);
            float r2 = Random.Range(-1f, 1f);
            wanderTarget += new Vector3(r1 * wanderJitter, 0.0f, r2 * wanderJitter);
            wanderTarget.Normalize();
            wanderTarget *= wanderRadius;

            Vector3 targetLocal = wanderTarget + new Vector3(0, 0, wanderDistance);
            Vector3 targetWorld = vehicle.CacheTransform.localToWorldMatrix.MultiplyPoint3x4(targetLocal);

            return targetWorld - vehicle.CacheTransform.position;

        }
    }
}